#include <iostream>
#include <utility>

using namespace std;

// 友元是赋予全局函数、类的成员函数、类有访问别的类的私有成员的权限
// 注意:
// 1. 友元函数不是类的成员函数
// 2. 友元的关系不能被继承
// 3. 友元的关系是单向的  类A是类B的友元, 但是类B不一定是类A的友元
// 4. 友元的关系不具有传递性  类B是类A的友元, 类C是类B的友元, 但是类C不一定是类A的友元

class Teacher;

class Student {
    friend void test01(); // 将 test01 全局函数设置为 Student 类的友元函数

    friend class Teacher; // 将 Teacher 类设置为 Student 类的友元类

public:
    Student() = default;

    Student(string name, int uuid) : name(std::move(name)), uuid(uuid) {}

public:
    string name;

private:
    int uuid = 10001;
};


void test01() {
    Student blue("blue", 10001);

    // 对象 public 的成员变量可以正常访问
    cout << "blue.name = " << blue.name << endl;

    // 对象 private 的成员变量可以被赋予友元的全局函数正常访问
    cout << "blue.uuid = " << blue.uuid << endl;

    cout << endl;
}

class Teacher {
public:
    Teacher() = default;

    Teacher(string name, int uuid) : name(std::move(name)), uuid(uuid) {}

public:
    string name;

private:
    int uuid = 10001;

public:
    Student *p = nullptr;

public:
    void accessStudentName(const Student &student) {
        cout << "stduent.name = " << student.name << endl;
    }

    void accessStudentUuid(const Student &student) {
        cout << "student.uuid = " << student.uuid << endl;
    }

    void pointerAccessStudentName() {
        cout << "p->name = " << p->name << endl;
    }

    void pointerAccessStudentUuid() {
        cout << "p->uuid = " << p->uuid << endl;
    }
};

void test02() {
    Student blue("blue", 10001);
    Teacher yui("yui", 10002);

    // Teacher 类可以正常访问 Student 类 public 的成员变量
    yui.accessStudentName(blue);

    // 被赋予友元的 Teacher 类可以正常访问 Student 类 private 的成员变量
    yui.accessStudentUuid(blue);

    cout << endl;
}

void test03() {
    Student blue("blue", 10001);
    Teacher yui("yui", 10002);

    yui.p = &blue;

    // 指针访问 Student 类 public 的成员变量
    yui.pointerAccessStudentName();

    // 被赋予友元的 Teacher 类指针访问 Student 类 private 的成员变量
    yui.pointerAccessStudentUuid();

    cout << endl;
}

// 类的成员函数(难点, 不是重点)

int main() {
    test01();

    test02();

    test03();
}